import 'dart:math';

import 'package:flutter/material.dart';

class PageFlipper extends StatefulWidget {
  final WidgetBuilder frontBuilder;
  final WidgetBuilder backBuilder;
  PageFlipper({Key? key, required this.frontBuilder, required this.backBuilder})
      : super(key: key);

  @override
  PageFlipperState createState() => PageFlipperState();
}

class PageFlipperState extends State<PageFlipper>
    with TickerProviderStateMixin {
  late AnimationController _controller;
  bool _showFront = true;

  @override
  void initState() {
    _controller = new AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500));
    _controller.addStatusListener(_updateStatus);
    super.initState();
  }

  void _updateStatus(AnimationStatus status) {
    if (status == AnimationStatus.completed ||
        status == AnimationStatus.dismissed) {
      setState(() {
        _showFront = !_showFront;
      });
    }
  }

  void flip() {
    if (_showFront) {
      _controller.forward();
    } else {
      _controller.reverse();
    }
  }

  @override
  void dispose() {
    _controller.removeStatusListener(_updateStatus);
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return PageFlipperAnimationBuilder(
      frontBuilder: widget.frontBuilder,
      backBuilder: widget.backBuilder,
      animation: _controller,
    );
  }
}

class PageFlipperAnimationBuilder extends StatelessWidget {
  final WidgetBuilder frontBuilder;
  final WidgetBuilder backBuilder;
  final Animation<double> animation;
  PageFlipperAnimationBuilder(
      {required this.frontBuilder,
      required this.backBuilder,
      required this.animation,
      Key? key})
      : super(key: key);

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
        animation: animation,
        builder: (context, widget) {
          bool showFront = animation.value < 0.5;
          final child =
              showFront ? frontBuilder(context) : backBuilder(context);
          double radiusAngle = animation.value * pi;
          double rotationAngle = showFront ? radiusAngle : pi - radiusAngle;
          var tilt = (animation.value - 0.5).abs() - 0.5;
          //设置一个小的偏移量，使得旋转时有一定的倾斜角度，看起来更有立体感
          tilt *= showFront ? -0.003 : 0.003;
          return Transform(
            transform: Matrix4.rotationY(rotationAngle)..setEntry(3, 0, tilt),
            child: child,
            alignment: Alignment.center,
          );
        });
  }
}
